﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;

namespace Heimdallr.Net
{   
    /// <summary>
    /// Provides basic definitions for <see cref="HttpRequestContext"/> and <see cref="HttpResponseContext"/> classes.
    /// </summary>
    public abstract class HttpOperationContext : IDisposable
    {
        #region Field

        private readonly HttpOperationDescription description;

        private bool disposed;

        #endregion

        #region Property

        /// <summary>
        /// Gets the description of current request or response context.
        /// </summary>
        public HttpOperationDescription Description
        {
            get { return this.description; }
        }

        /// <summary>
        /// Get the property indicates the instanse has disposed or not.
        /// </summary>
        public bool IsDisposed
        {
            get { return this.disposed; }
        }

        #endregion

        #region Constructor

        internal HttpOperationContext(string username, EndPoint remoteEp, Uri requestUri)
        {
            this.description = new HttpOperationDescription(username, remoteEp, requestUri);
            this.disposed = false;
        }

        #endregion

        #region IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
            this.disposed = true;
        }

        #endregion

        #region Internal Method

        internal TResult TryGet<TResult>(Func<TResult> func)
        {
            try
            {
                return func();
            }
            catch (ObjectDisposedException error)
            {
                this.disposed = true;
                throw new ObjectDisposedException("This object is closed.", error);
            }
            catch (Exception error)
            {
                throw error;
            }
        }

        internal TResult TryGet<T, TResult>(Func<T, TResult> func, T arg)
        {
            try
            {
                return func(arg);
            }
            catch (ObjectDisposedException error)
            {
                this.disposed = true;
                throw new ObjectDisposedException("This object is closed.", error);
            }
            catch (Exception error)
            {
                throw error;
            }
        }

        internal void TrySet<T>(Action<T> action, T arg)
        {
            try
            {
                action(arg);
            }
            catch (ObjectDisposedException error)
            {
                this.disposed = true;
                throw new ObjectDisposedException("This object is closed.", error);
            }
            catch (Exception error)
            {
                throw error;
            }
        }

        #endregion

        #region Others

        internal static DateTime? GetDateTimeValue(string value)
        {
            DateTime result;

            if (!string.IsNullOrWhiteSpace(value) && DateTime.TryParseExact(value, "r", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
                return result;
            else
                return null;
        }

        internal static string SetDateTimeValue(DateTime? value)
        {
            return value.HasValue ? default(string) : value.Value.ToString("r");
        }

        #endregion
    }

    /// <summary>
    /// Provides informations of current request or response context.
    /// </summary>
    public class HttpOperationDescription
    {
        #region Field

        private readonly string username;
        private readonly EndPoint remoteEp;
        private readonly Uri requestUri;

        private object tag;

        #endregion

        #region Property

        /// <summary>
        /// Gets the user name which represents current client.  
        /// </summary>
        public string UserName
        {
            get { return this.username; }
        }

        /// <summary>
        /// Gets the client IP address and port number from which the request originated.
        /// </summary>
        public EndPoint RemoteEndPoint
        {
            get { return this.remoteEp; }
        }

        /// <summary>
        /// Gets the URI requested by the client.
        /// </summary>
        public Uri RequestUri
        {
            get { return this.requestUri; }
        }

        /// <summary>
        /// Get or set the user-defined data.
        /// </summary>
        public object Tag
        {
            get { return this.tag; }
            set { this.tag = value; }
        }

        #endregion

        #region Constructor

        internal HttpOperationDescription(string username, EndPoint remoteEp, Uri requestUri)
        {
            this.username = username;
            this.remoteEp = remoteEp;
            this.requestUri = requestUri;
        }

        #endregion
    }
}